#include <jni.h>
#include <android/log.h>
#include <cstdio>
#include <ctime>
#include <malloc.h>
#include <cstring>

#include "jvmti.h"
#include "MemoryFile.h"

//
// Created by lenovo on 2021/9/10.
//



jvmtiEnv *mJvmtiEnv;
MemoryFile *memoryFile;
jlong tag = 0;

long long getCurrentMillis(){
    struct timeval tv{};
    gettimeofday(&tv,nullptr);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

extern "C"
JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char* options, void* reserved){
    // 获取JVMTI环境
    vm->GetEnv(reinterpret_cast<void **>(&mJvmtiEnv), JVMTI_VERSION_1_2);

    // 开启jvmti能力
    jvmtiCapabilities capabilities;
    mJvmtiEnv->GetPotentialCapabilities(&capabilities);
    mJvmtiEnv->AddCapabilities(&capabilities);

    return JNI_OK;
}

extern "C"
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv *env;
    if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK){
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

// 对象分配内存
extern "C"
JNIEXPORT void JNICALL
VMObjectAlloc(jvmtiEnv *jvmti_env,
        JNIEnv* jni_env,
        jthread thread,
        jobject object,
        jclass object_klass,
        jlong size){
    //给对象打tag，后续在objectFree()内可以通过该tag来判断是否成对出现释放
    jvmti_env->SetTag(object, ++tag);
    //获取线程信息
    jvmtiThreadInfo threadInfo;
    jvmti_env->GetThreadInfo(thread, &threadInfo);
    //获得 创建的对象的类签名
    char *classSignature;
    jvmti_env->GetClassSignature(object_klass, &classSignature, nullptr);

    long long time = getCurrentMillis();
    //获得堆栈信息
    char *info;
    asprintf(&info,"ObjectAlloc|Time:%lld Thread:%s Class:%s Size:%lld Tag：%lld\n",
             time,threadInfo.name, classSignature, size, tag);
    if(memoryFile != nullptr){
        memoryFile->write(info,strlen(info));
    }
}

// 对象释放
extern "C"
JNIEXPORT void JNICALL
ObjectFree(jvmtiEnv *jvmti_env,jlong tag){
    char *info;
    long long time = getCurrentMillis();
    asprintf(&info,"ObjectFree|Time:%lld Tag:%lld\n",time,tag);
    if(memoryFile != nullptr){
        memoryFile->write(info,strlen(info));
    }

}

// 进入方法
extern "C"
JNIEXPORT void JNICALL
MethodEntry(jvmtiEnv *jvmti_env,
         JNIEnv* jni_env,
         jthread thread,
         jmethodID method){

}
// 退出方法
extern "C"
JNIEXPORT void JNICALL
MethodExit(jvmtiEnv *jvmti_env,
        JNIEnv* jni_env,
        jthread thread,
        jmethodID method,
        jboolean was_popped_by_exception,
        jvalue return_value){

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_jvmti_jni_JVMTIAgent_agentInit(JNIEnv *env, jclass clazz,jstring _path) {

    const char *path = env->GetStringUTFChars(_path,nullptr);
    char *filepath;

    long long millis = getCurrentMillis();
    asprintf(&filepath,"%s/%lld.log",path,millis);

    memoryFile = new MemoryFile(filepath);
    free(filepath);

    env->ReleaseStringUTFChars(_path,path);

    jvmtiEventCallbacks callbacks;
//    memset(&callbacks,0,sizeof(callbacks));
    callbacks.MethodEntry = &MethodEntry;
    callbacks.MethodExit = &MethodExit;
    callbacks.VMObjectAlloc = &VMObjectAlloc;
    callbacks.ObjectFree = &ObjectFree;

    mJvmtiEnv->SetEventCallbacks(&callbacks,sizeof(callbacks));

    mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_METHOD_ENTRY,NULL);
    mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_METHOD_EXIT,NULL);
    mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_VM_OBJECT_ALLOC,NULL);
    mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_OBJECT_FREE,NULL);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_jvmti_jni_JVMTIAgent_agentRelease(JNIEnv *env, jclass clazz) {
    mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE,JVMTI_EVENT_METHOD_ENTRY,NULL);
    mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE,JVMTI_EVENT_METHOD_EXIT,NULL);
    mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE,JVMTI_EVENT_VM_OBJECT_ALLOC,NULL);
    mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE,JVMTI_EVENT_OBJECT_FREE,NULL);
}